/**
 * 
 */
package com.thor.palmmall.util.billnumber;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import javax.persistence.Query;

import org.hibernate.ejb.HibernateEntityManager;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.thor.commons.util.Assert;
import com.thor.dao.BasicDao;

/**
 * @author Administrator
 * 
 */
public class OracleSequenceGenerator extends  BasicDao implements
		ISequenceGenerator, Serializable {

	private static final long serialVersionUID = 5841757695563887379L;

	/** 序列的前缀 */
	public static final String PREFIX_SEQ = "SPSeq";
	/** 序列名需要满足的规则 */
	private static final String PATTERN_DBID = "^[\\w]+$";
	/** 序列名的最大长度 */
	private static final int MAX_DBID_LENGTH = 24;

	@Transactional(rollbackFor = Exception.class, propagation = Propagation.NOT_SUPPORTED)
	public long nextValue(SequenceSpec seq) throws Exception {
		Assert.assertArgumentNotNull(seq, "seq");
		Assert.assertArgumentNotNull(seq.getDbId(), "seq.dbid");

		if (!java.util.regex.Pattern.matches(PATTERN_DBID, seq.getDbId()))
			throw new Exception("数据库序列标识只能由数字或字母组成。");
		if (seq.getDbId().length() > MAX_DBID_LENGTH)
			throw new Exception("数据库序列标识不能超过 " + MAX_DBID_LENGTH + " 位。");
		if (seq.getStartValue() <= 0)
			throw new Exception("数据库序列的起始值必须大于0。");

		try {
			return fetchNext(seq);
		} catch (Exception e) {
			tryCreateSeq(seq);
			return fetchNext(seq);
		}
	}

	private long fetchNext(SequenceSpec seq) throws Exception {
		try {
			Query query = em.createNativeQuery("select " + getSeqName(seq)
					+ ".nextVal from dual");
			List list = query.getResultList();
			if (list.isEmpty())
				throw new Exception("取下一流水号失败。");
			return ((Number) list.get(0)).longValue();
		} catch (Exception e) {
			if (reachMaxValue(e))
				throw new Exception("达到最大流水号，无法再继续分配。");
			throw new Exception(e);
		}
	}

	private boolean reachMaxValue(Exception e) {
		if (e.getMessage() != null && e.getMessage().contains("ORA-08004:"))
			return true;
		return false;
	}

	private void tryCreateSeq(SequenceSpec seq) {
		try {
			HibernateEntityManager hem = (HibernateEntityManager) em;
			Connection dbConn = hem.getSession().connection();
			Statement st = null;
			try {
				st = dbConn.createStatement();
				st.executeUpdate("create sequence " + getSeqName(seq)
						+ " increment by " + seq.getIncrement()
						+ " start with " + seq.getStartValue() + " maxValue "
						+ seq.getMaxValue() + " nocycle nocache");
			} catch (SQLException e) {
				LoggerFactory.getLogger(this.getClass()).error("创建序列失败。", e);
				throw e;
			} finally {
				if (st != null)
					st.close();
				hem.getSession().disconnect();
			}

		} catch (Exception e) {
			logger.error("创建序列失败。", e);
		}
	}

	private String getSeqName(SequenceSpec seq) throws Exception {
		return PREFIX_SEQ + seq.getDbId();
	}
}
